﻿

using XT.Common.Enums;
using XT.Common.Extensions;
using XT.Common.Services;
using HbcServerProcess.Interfaces;
using HbcServerProcess.Models;
using IotContract.Consts;
using IotContract.Dtos.Clients;
using IotContract.Dtos.Servers.Process;
using IotContract.Dtos.Servers.Pushs;
using IotContract.Enums;
using IotContract.Extensions;
using IotContract.Interfaces;
using IotContract.Interfaces.Fmt;
using IotContract.Interfaces.Process;
using IotContract.Models;
using IotContract.Models.Clients.Hks;
using IotContract.Models.Fmts;
using IotContract.Models.Jobs;
using IotContract.Models.Plcs;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HbcServerProcess.DataProcess;
public class StackerDataProcess : IServerProcessHandle
{
    public string ProcessName { get; set; }=HbcServerConst.ProcessName;
    public IotEquipType ProcessType { get; set; } = IotEquipType.STACKER;

    /// <summary>
    /// 正在执行的堆垛机任务,容错处理
    /// </summary>
    private ConcurrentDictionary<int, PlcSCAddr> RunTaskDic = new ConcurrentDictionary<int, PlcSCAddr>();
    /// <summary>
    /// 报警弹窗
    /// </summary>
    private ConcurrentBag<IotEquipWarnDto> _warns = new ConcurrentBag<IotEquipWarnDto>();


    private readonly IServerMainManager _mainProcess;
    private readonly IProcessDataService _processDataService;
    private readonly ILogService _logService;
    private readonly IRemoteHttpService _remoteHttpService;
    private readonly IProcessJobService _jobService;
    private readonly IEquipValidateService _equipValidateService;

    public StackerDataProcess(IServerMainManager serverMainManager, IProcessDataService processDataService, ILogService logService, IRemoteHttpService remoteHttpService,
        IProcessJobService processJobService,IEquipValidateService equipValidateService)
    {
        _mainProcess = serverMainManager;
        _processDataService = processDataService;
        _remoteHttpService = remoteHttpService;
        _jobService = processJobService;
        _logService = logService;
        _equipValidateService = equipValidateService;
        
    }
    public async Task<bool> ParsingData()
    {
        // 获取数据
        var equips = await _mainProcess.GetEquips<PushSiemensS7Dto>(new IotContract.Models.Clients.ClientRequestCommand
        {
            RequestType = IotEquipType.STACKER.ToString(),
            ProtobufType = IotProtobufType.SIEMENS_S7.ToString()
        });

        
        var scUnitDic = _processDataService.ScUnits;
        // 堆垛机点位不需要设置UnitNum
        Parallel.ForEach(equips, equip =>
        {
           
            
            var tags = equip.Tags;
            ScUnitDto scUnit = null;
            if (scUnitDic.ContainsKey(equip.Num.To<int>()   ))
            {
                scUnit = scUnitDic[equip.Num.To<int>()];
            }
            else
            {
                scUnit = new ScUnitDto();
                scUnit.EquipNum = equip.Num.To<int>();
                scUnit.EquipId = equip.Id;
                scUnit.Connection = equip.Connection;
                
               
                scUnitDic.TryAdd(equip.Num.To<int>(), scUnit);
            }
           

            SetUnitTag(scUnit, tags);

        });

        return true;

    }


    /// <summary>
    /// 取值
    /// </summary>
    /// <param name="scUnit"></param>
    /// <param name="tags"></param>
    private void SetUnitTag(ScUnitDto scUnit, List<PushEquipTag> tags)
    {
        scUnit.Tags = tags;
        Parallel.ForEach(scUnit.Tags, tag =>
        {
            int.TryParse(tag.Value, out int result);
            switch (tag.TagProperty)
            {
                case "G_MODE":
                    {

                        scUnit.SC_Mode = result;
                        break;
                    }
                // State==2  IsWorking
                case "STATE":
                    {

                        scUnit.SC_State = result;
                        break;
                    }
                case "BAY":
                    {

                        scUnit.SC_Bay = result;
                        break;
                    }
                case "LEVEL":
                    {
                        scUnit.SC_Level = result;
                        break;
                    }
                case "FORK1_POS":
                    {
                        scUnit.SC_Fork1Pos = result;
                        break;
                    }
                case "FORK2_POS":
                    {
                        scUnit.SC_Fork2Pos = result;
                        break;
                    }
                case "ERROR_CODE":
                    {

                        scUnit.ErrorCode = result;
                        break;
                    }
                case "CARRI_EXIST_FORK1":
                    {
                       

                       
                        scUnit.IsExistTray1 = result == 1;
                        break;
                    }
                case "CARRI_EXIST_FORK2":
                    {
                        scUnit.IsExistTray2 = result == 1;
                        break;
                    }
                case "ACTIVE":
                    {
                        scUnit.Active = result;
                        break;
                    }
                case "FORK1_JOBEND":
                    {
                        scUnit.JobEndFork1 = result;

                        break;
                    }
                case "FORK2_JOBEND":
                    {
                        scUnit.JobEndFork2 = result;

                        break;
                    }
                case "R_JOBTYPE":
                    {
                        scUnit.JobType = result;
                        break;
                    }
                case "R_JOBNUMBER":
                    {

                        scUnit.JobNumber = result;

                        
                        break;
                    }
                case "W_JOBNUMBER":
                    {
                        scUnit.ReadTime = tag.ReadTime;
                        scUnit.W_JobNumber = result;
                        break;
                    }
                case "R_FROM_BANK":
                    {
                        scUnit.FromBank = result;
                        break;
                    }
                case "R_FROM_BAY":
                    {
                        scUnit.FromBay = result;
                        break;
                    }
                case "R_FROM_LEVEL":
                    {
                        scUnit.FromLevel = result;
                        break;
                    }
                case "R_FROM_STATION":
                    {
                        scUnit.FromStation = result;
                        break;
                    }
                case "R_TO_BANK":
                    {
                        scUnit.ToBank = result;
                        break;
                    }
                case "R_TO_BAY":
                    {
                        scUnit.ToBay = result;
                        break;
                    }
                case "R_TO_LEVEL":
                    {
                        scUnit.ToLevel = result;
                        break;
                    }
                case "R_TO_STATION":
                    {
                        scUnit.ToStation = result;
                        break;
                    }
                case "R_JOBNUMBER_FORK2":
                    {

                        scUnit.JobNumberFork2 = result;
                        break;
                    }
                case "R_FROM_BANK_FORK2":
                    {
                        scUnit.FromBankFork2 = result;
                        break;
                    }
                case "R_FROM_BAY_FORK2":
                    {
                        scUnit.FromBayFork2 = result;
                        break;
                    }
                case "R_FROM_LEVEL_FORK2":
                    {
                        scUnit.FromLevelFork2 = result;
                        break;
                    }
                case "R_FROM_STATION_FORK2":
                    {
                        scUnit.FromStationFork2 = result;
                        break;
                    }
                case "R_TO_BANK_FORK2":
                    {
                        scUnit.ToBankFork2 = result;
                        break;
                    }
                case "R_TO_BAY_FORK2":
                    {
                        scUnit.ToBayFork2 = result;
                        break;
                    }
                case "R_TO_LEVEL_FORK2":
                    {
                        scUnit.ToLevelFork2 = result;
                        break;
                    }
                case "R_TO_STATION_FORK2":
                    {
                        scUnit.ToStationFork2 = result;
                        break;
                    }
            }
        });
    }
    /// <summary>
    /// 处理数据
    /// </summary>
    /// <returns></returns>
    public async Task<bool> ProcessData()
    {

        var scUnitDic = _processDataService.ScUnits;
        ConcurrentDictionary<int, EquipWarnStatus> equipWarnDic = new ConcurrentDictionary<int, EquipWarnStatus>();
        // Sc状态赋值
        if (_processDataService.EcsEquipWarnDic.ContainsKey(ProcessType))
        {
            equipWarnDic = _processDataService.EcsEquipWarnDic[ProcessType];
        }
        else
        {
            _processDataService.EcsEquipWarnDic.TryAdd(ProcessType, equipWarnDic);
        }

        foreach (var scUnit in scUnitDic.Values)
        {
            var tag = scUnit;
            EquipWarnStatus equipWarn = new EquipWarnStatus();

            if (equipWarnDic.ContainsKey(scUnit.EquipNum))
            {
                equipWarn = equipWarnDic[scUnit.EquipNum];
            }
            else
            {
                equipWarnDic.TryAdd(scUnit.EquipNum, equipWarn);
            }
            // 开始判断状态
            // 模式为未联机和自动
            equipWarn.RecordTime = DateTime.Now;
            equipWarn.OldStatus = equipWarn.Status;
            equipWarn.Status = tag.SC_State;
            equipWarn.OldErrorCode = equipWarn.ErrorCode;
            equipWarn.Number = tag.EquipNum.ToString();
            equipWarn.ErrorCode = tag.ErrorCode.ToString();
            if (tag.ErrorCode > 0)
            {
                equipWarn.Status = 3;
            }
            // 不是实时数据，未联机掉线
            if (!tag.IsRealTime())
            {
                equipWarn.Status = 0;
            }

        }

        return true;
    }
    /// <summary>
    /// 开始进行调度
    /// </summary>
    /// <returns></returns>
    public async Task<bool> ProcessOrder()
    {
        // 入库
        await OrderEnter();

        // 出库
        await OrderOut();

        // 移库
        await OrderMove();

        // 容错
        await OrderRun();

        return true;
    }
    /// <summary>
    /// 任务容错启动
    /// </summary>
    /// <returns></returns>
    private async Task OrderRun()
    {
        List<int> removeKeys = new List<int>();
        foreach (var plcSc in RunTaskDic.Values)
        {
            var sctag = _processDataService.ScUnits.GetValueOrDefault(plcSc.GroupNumber);

            if (sctag == null || sctag.SC_State == 2)
            {
                removeKeys.Add(plcSc.JobNumber);
                continue;
            }
            // 处于等待且大于10s，重写一次堆垛机
            if (sctag.SC_State == 1 && DateTime.Now.Subtract(plcSc.WriteTime.Value).TotalSeconds > 10)
            {
                try
                {
                   await _processDataService.WriteScAddr(sctag, plcSc);
                    _logService.Log($"再次启动堆垛机 {plcSc.GroupNumber} {plcSc.JobNumber} 任务类型 {plcSc.ActionType} ");
                }
                catch (Exception ex)
                {
                    _logService.Log($"再次启动堆垛机失败,{ex.Message}");
                }
                removeKeys.Add(plcSc.JobNumber);

            }
        }

        foreach (var key in removeKeys)
        {
            RunTaskDic.TryRemove(key, out _);
        }
    }
    /// <summary>
    /// 报警状态分拆
    /// </summary>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public async Task<bool> ProcessWarn()
    {
        // 上传报警
        if (_warns.Count > 0)
        {
            _warns.ForEach(x =>
            {
                x.ProtobufType = IotProtobufType.HBC_SIEMENS_S7.ToString();
            });
            await _mainProcess.AddWarns(_warns);
            _warns.Clear();
        }
        return true;
    }


    #region 控制逻辑



    /// <summary>
    /// 控制入库
    /// </summary>
    /// <returns></returns>
    private async Task OrderEnter()
    {
        var jobDic = _jobService.ScEnterJobDic;
        var jobOutDic = _jobService.ScOutJobDic;

        if (jobDic.Count == 0)
        {
            return;
        }
        // 根据设备分组
        var scJobs = jobDic.Values.ToLookup(x => x.EquipNum).ToDictionary(x => x.Key, y => y.OrderBy(x => x.CreateTime).ToList());

        foreach (var sc in scJobs.Keys)
        {
            var jobs = scJobs[sc];
            if (jobs != null && jobs.Count > 0)
            {
                var job = jobs.FirstOrDefault();
                if (job == null) continue;
                // 未启动
                if (job.Status == 0)
                {
                    var sameSc = jobDic.Values.FirstOrDefault(x => x.Status == 1 && x.EquipNum == job.EquipNum);


                    if (sameSc != null)
                    {
                        // 相同堆垛机正在运行中，不同时启动
                        continue;
                    }
                    // 如果存在火警任务，先不进行入库
                    var outSc = jobOutDic.Values.FirstOrDefault(x => x.EquipNum == job.EquipNum && x.ToStation == 999);

                    if (outSc != null)
                    {
                        continue;
                    }

                    await ScRunEnter(job);
                }
                // 正在运行中，运行完成后提交数据
                else if (job.Status == 1)
                {
                    await ScEnterComplete(job);
                }
            }
        }


    }

    /// <summary>
    /// 控制移库
    /// </summary>
    /// <returns></returns>
    private async Task OrderMove()
    {
        var jobMoveDic = _jobService.ScMoveJobDic;
        var jobDic = _jobService.ScEnterJobDic;
        var jobOutDic = _jobService.ScOutJobDic;

        if (jobMoveDic.Count == 0)
        {
            return;
        }
        var scJobs = jobMoveDic.Values.ToLookup(x => x.EquipNum).ToDictionary(x => x.Key, y => y.OrderBy(x => x.CreateTime).ToList());

        foreach (var sc in scJobs.Keys)
        {
            var jobs = scJobs[sc];
            if (jobs != null && jobs.Count > 0)
            {
                var job = jobs.FirstOrDefault();
                if (job == null) continue;
                // 未启动
                if (job.Status == 0)
                {
                    var sameSc = jobDic.Values.FirstOrDefault(x => x.Status == 1 && x.EquipNum == job.EquipNum);


                    if (sameSc != null)
                    {
                        // 相同堆垛机正在运行中，不同时启动
                        continue;
                    }
                    // 如果存在火警任务，先不进行入库
                    var outSc = jobOutDic.Values.FirstOrDefault(x => x.EquipNum == job.EquipNum && x.ToStation == 999);

                    if (outSc != null)
                    {
                        continue;
                    }

                    await ScMoveEnter(job);
                }

                // 正在运行中，运行完成后提交数据
                else if (job.Status == 1)
                {
                    await ScMoverComplete(job);
                }
            }
        }


    }
    /// <summary>
    /// 控制出库
    /// </summary>
    /// <returns></returns>
    private async Task OrderOut()
    {
        var jobDic = _jobService.ScOutJobDic;

        if (jobDic.Count == 0)
        {
            return;
        }
        var scJobs = jobDic.Values.ToLookup(x => x.EquipNum).ToDictionary(x => x.Key, y => y.OrderBy(x => x.CreateTime).ToList());

        foreach (var sc in scJobs.Keys)
        {
            var jobs = scJobs[sc];
            if (jobs != null && jobs.Count > 0)
            {
                // job取火警
                var job = jobs.FirstOrDefault(x => x.ToStation == 999);
                if (job == null)
                {
                    job = jobs.FirstOrDefault();
                }

                if (job == null) continue;


                // 未启动
                if (job.Status == 0)
                {
                    var sameSc = jobDic.Values.FirstOrDefault(x => x.Status == 1 && x.EquipNum == job.EquipNum);

                    if (sameSc != null)
                    {
                        // 火警任务超前，完成job2
                        await ScOutComplete(sameSc);
                        continue;



                    }
                    await ScOutEnter(job);
                }

                // 正在运行中，运行完成后提交数据
                else if (job.Status == 1)
                {
                    await ScOutComplete(job);
                }
            }
        }

    }
    /// <summary>
    /// 出库启动
    /// </summary>
    /// <returns></returns>
    private async Task<bool> ScOutEnter(ScOutJob job)
    {
        try
        {
            var sc = _processDataService.ScUnits.GetValueOrDefault(job.EquipNum);

            if (sc == null || sc.JobID.IsNotNullOrEmpty())
            {
                return false;
            }

            if (sc.JobEndFork1 == 0 || sc.JobEndFork2 == 0 || sc.SC_State != 1)
            {
                return false;
            }

            // 堆垛机没有复位
            if (sc.W_JobNumber != 0 && sc.W_JobNumber != 9999)
            {
                return false;
            }

            if(_equipValidateService.ValidateStacker(sc) == false)
            {
                return false;
            }
            // 火警任务
            if (job.ToStation == 999)
            {
             




                var fireSc = new PlcSCAddr
                {
                    ActionType = 9,
                    ToStation = job.ToStation,
                    JobNumber = job.JobNumber,
                    FromBank = job.FromBank,
                    FromBay = job.FromBay,
                    FromLevel = job.FromLevel,
                    GroupNumber = job.EquipNum
                };
                // 堆垛机正常，直接调用堆垛机
               await _processDataService.WriteScAddr(sc,fireSc);

                fireSc.WriteTime = DateTime.Now;

                if (!RunTaskDic.ContainsKey(fireSc.JobNumber))
                {
                    RunTaskDic.TryAdd(fireSc.JobNumber, fireSc);
                }

                sc.JobID = job.JobID;

                job.Status = 1;


                // 出库任务
                sc.ActionType = 2;

                await _logService.LogContent($"{job.ToString()} fire exec success", LogEnum.Info, job.EquipNum.ToString());

                return true;



            }


          

            // 刚做了出库
            if (sc.ActionType == 2)
            {
                // 找到入库任务
                var scenterJob = _jobService.ScEnterJobDic.Values.OrderBy(x => x.CreateTime).FirstOrDefault(x => x.EquipNum == job.EquipNum && x.Status == 0 && x.IsError == 0);
                var scmoveJob = _jobService.ScMoveJobDic.Values.OrderBy(x => x.CreateTime).FirstOrDefault(x => x.EquipNum == job.EquipNum && x.Status == 0 && x.IsError == 0);
                if (scenterJob != null)
                {
                    // 入库时间小于出库，优先入库
                    if (scenterJob.CreateTime < job.CreateTime)
                    {
                        return false;
                    }

                }
                if (scmoveJob != null)
                {
                    // 入库时间小于出库，优先移库
                    if (scmoveJob.CreateTime < job.CreateTime)
                    {
                        return false;
                    }
                }
            }


            var tag = _processDataService.CvUnitTags.GetValueOrDefault(job.ToTagNumber);
             

            if (tag==null || tag.ErrorCode > 0)
            {
                return false;
            }

            string key = string.Empty;

            if (job.ToTagNumber2 > 0)
            {
                var tag2 = _processDataService.CvUnitTags.GetValueOrDefault(job.ToTagNumber2);



                // 站台是否有托盘
                if (tag2.IsExistTray1 || tag2.JobNumber > 0)
                {
                    return false;
                }
                key = $"{job.JobID}-{job.JobID2}";
            }
            else
            {
                key = job.JobID;
            }

            // 站台是否有托盘
            if (tag.IsExistTray1 || tag.JobNumber > 0)
            {

                return false;

            }          

            // 查询job的状态，确认是否运行
            var scRemote = await _remoteHttpService.GetScRemoteTask(job.JobID);
            if (scRemote.Data == null && scRemote.Code == 100)
            {
                return false;
            }
            if (scRemote.Data == null)
            {
                // await _serverDataService.RemoveOutJob(job);
                job.IsError = 888;
                job.ErrorMsg = "查询不到FMS任务，无法启动";
                return false;
            }
            else if (scRemote.Data.TaskStatus != 1)
            {
                job.ErrorMsg = $"FMS任务状态为{scRemote.Data.TaskStatus}，无法启动";
                job.IsError = 888;
                return false;
            }

         


            job.Data = string.Join(',', scRemote.Data.TrayNos.Where(x => x.IsNotNullOrEmpty()));

            int randomJobNumber = job.JobNumber;
            var plcSc = new PlcSCAddr
            {
                ActionType = 2,
                ToStation = job.ToStation,
                JobNumber = randomJobNumber,
                FromBank = job.FromBank,
                FromBay = job.FromBay,
                FromLevel = job.FromLevel,
                GroupNumber = job.EquipNum,

                ToStation2 = job.ToStation2,
                JobNumber2 = job.JobNumber2,
                FromBank2 = job.FromBank2,
                FromBay2 = job.FromBay2,
                FromLevel2 = job.FromLevel2,


            };
           await _processDataService.WriteScAddr(sc,plcSc);

            plcSc.WriteTime = DateTime.Now;

            if (!RunTaskDic.ContainsKey(plcSc.JobNumber))
            {
                RunTaskDic.TryAdd(plcSc.JobNumber, plcSc);
            }

            sc.JobID = job.JobID;

            job.Status = 1;

            job.RunTime = DateTime.Now;

            // 出库任务
            sc.ActionType = 2;

            await _logService.LogContent($"{job.ToString()} stacker out run success",  LogEnum.Info, job.EquipNum.ToString());
            //// 入库任务添加到缓存
            //await _cacheService.SetCacheAsync(key, job, TimeSpan.FromDays(1));

            return true;
        }
        catch (Exception ex)
        {
            job.ErrorMsg = $"run error,{ex.Message}";

            return false;
        }

    }
    /// <summary>
    /// 写入Ecs数据
    /// </summary>
    /// <param name=""></param>
    /// <returns></returns>
    public async Task<string> StackerWriteData(EcsWriteData data)
    {
        string msg = string.Empty;
        try
        {
           
            if (data.CvData != null)
            {

             msg=  await _processDataService.WriteCvAddr(new EcsCvWrite
                {
                    DestNum = data.CvData.DestNum,
                    JobNumber = data.CvData.JobNumber,
                    JobType = data.CvData.JobType,
                    TagNumber = data.CvData.TagNumber,
                    TrayType = data.CvData.TrayType
                });
            }
            if (data.ChargeData != null)
            {
                msg = await _processDataService.WriteOcvFmtAddr(
                          new HkWriteInstruct
                          {
                              Num = data.ChargeData.GroupNumber.ToString(),
                              Stage = data.ChargeData.Stage,
                              Command = data.ChargeData.Command
                          }
                      );
            }
            return msg;
           
        }
        catch (Exception ex)
        {
            return ex.Message;
        }
    }

    /// <summary>
    /// 出库完成
    /// </summary>
    /// <returns></returns>
    private async Task ScOutComplete(ScOutJob job)
    {

        if (job.RetryCount > 0)
        {
            return;
        }

        try
        {



            var sctag = _processDataService.ScUnits.GetValueOrDefault(job.EquipNum);

            if (sctag == null)
            {
                return;
            }

            if (job.CreateTime != null && DateTime.Now.Subtract(job.CreateTime.Value).TotalMinutes > 2)
            {
                job.IsError = sctag.ErrorCode;
            }
            // 15s内不可能完成
            if (DateTime.Now.Subtract(job.RunTime).TotalSeconds < 15)
            {
                return;
            }

            // 火警任务区别对待
            if (job.ToStation == 999)
            {
                if (sctag.SC_State == 1 && sctag.ErrorCode == 0 && sctag.JobEndFork2 == 1 && sctag.JobEndFork1 == 1 && sctag.W_JobNumber == job.JobNumber && sctag.JobID == job.JobID)
                {
                    await _logService.LogContent($"{job.ToString()},stacker fire out finish success",  LogEnum.Info, job.EquipNum.ToString());
                    // 成功后 清除缓存
                    job.Status = 2;
                    sctag.JobID = String.Empty;
                    sctag.JobIDOld = sctag.JobID;
                    if (_jobService.ScOutJobDic.ContainsKey(job.JobID))
                    {
                        _jobService.ScOutJobDic.TryRemove(job.JobID, out _);
                    }
                }
                return;
            }

            var totag =_processDataService.CvUnitTags.GetValueOrDefault(job.ToTagNumber);

            if (totag == null)
            {
                return;
            }


            string cacheKey = string.Empty;

            string dicKey = string.Empty;

            if (job.JobID2.IsNotNullOrEmpty())
            {
                cacheKey = $"{job.JobID}-{job.JobID2}";
                dicKey = $"{job.JobID}-{job.JobID2}";
            }
            else
            {
                cacheKey = $"{job.JobID}";
                dicKey = job.JobID;
            }


            if (sctag.SC_State == 1 && sctag.ErrorCode == 0 && sctag.JobEndFork2 == 1 && sctag.JobEndFork1 == 1 && sctag.W_JobNumber == job.JobNumber && totag.IsExistTray1)
            {

                RemoteReturnModel<EcsWriteData> result2 = new RemoteReturnModel<EcsWriteData>()
                {
                    Flag = true
                };
                job.RetryCount++;

                // 无论成功失败，都算完成
                job.Status = 2;
                sctag.JobID = String.Empty;
                sctag.JobIDOld = sctag.JobID;
                Task.Factory.StartNew(async () =>
                {
                    // 双叉任务
                    if (job.JobID2.IsNotNullOrEmpty())
                    {
                        result2 = await _remoteHttpService.CommitScTaskID(new ScTaskDefine
                        {
                            TaskId = long.Parse(job.JobID2),
                            OptType = 2,
                            TaskNumber = job.JobNumber2
                        });
                    }






                    // 调用远程接口
                    var result = await _remoteHttpService.CommitScTaskID(new ScTaskDefine
                    {
                        TaskId = long.Parse(job.JobID),
                        OptType = 2,
                        TaskNumber = job.JobNumber
                    });

                    if (result.Flag && result2.Flag)
                    {
                        if (job.JobID2.IsNotNullOrEmpty() && result2.Data != null)
                        {
                            var msg2 = await StackerWriteData(result2.Data);
                            if (msg2.IsNotNullOrEmpty())
                            {
                                // 进行重试
                                await Task.Delay(500);
                                msg2 = await StackerWriteData(result2.Data);
                                if (msg2.IsNotNullOrEmpty())
                                {
                                    await Task.Delay(1000);
                                    // 再次重试
                                    msg2 = await StackerWriteData(result2.Data);
                                    if (msg2.IsNotNullOrEmpty())
                                    {
                                        job.ErrorMsg = $"{IotMsgConst.ScOutWriteError}，{msg2}";
                                        await _logService.LogContent($"{job.ToString()},{IotMsgConst.ScOutWriteError} {msg2} {JsonConvert.SerializeObject(result.Data)}",  LogEnum.Info, job.EquipNum.ToString(), true);

                                        _warns.Add(new IotEquipWarnDto
                                        {
                                            EquipType = ProcessType.ToString(),
                                            ErrorCode = IotMsgConst.ScOutWriteError + msg2,
                                            ErrorTime = DateTime.Now,
                                            EquipNum = job.EquipNum.ToString()
                                        });
                                       


                                    }
                                }


                            }
                        }

                        if (result.Data == null)
                        {
                            job.ErrorMsg = $"{IotMsgConst.ScRemoteError}";
                            job.IsError = 888;
                            return;
                        }
                        var msg = await StackerWriteData(result.Data);
                        if (msg.IsNotNullOrEmpty())
                        {
                            await Task.Delay(500);
                            msg = await StackerWriteData(result.Data);
                            if (msg.IsNotNullOrEmpty())
                            {
                                await Task.Delay(1000);
                                msg = await StackerWriteData(result.Data);
                                if (msg.IsNotNullOrEmpty())
                                {
                                    job.ErrorMsg = $"{IotMsgConst.ScOutWriteError}，{msg}";
                                    await _logService.LogContent($"{job.ToString()},堆垛机出库任务 物流线任务失败 {msg},{JsonConvert.SerializeObject(result.Data)}",  LogEnum.Error, job.EquipNum.ToString(), true);

                                   
                                  
                                    _warns.Add(new IotEquipWarnDto
                                    {
                                        EquipType = ProcessType.ToString(),
                                        ErrorCode = IotMsgConst.ScOutWriteError + msg,
                                        ErrorTime = DateTime.Now,
                                        EquipNum = job.EquipNum.ToString()
                                    });
                                    job.IsError = 888;
                                    return;
                                }
                            }


                        }
                        await _logService.LogContent($"{job.ToString()},堆垛机出库任务 通知完成状态成功",  LogEnum.Info, job.EquipNum.ToString());

                        if (_jobService.ScOutJobDic.ContainsKey(dicKey))
                        {
                            _jobService.ScOutJobDic.TryRemove(dicKey, out _);
                        }

                    }
                    else
                    {
                        job.ErrorMsg = $"{IotMsgConst.ScRemoteError}，{result.Msg} {result2.Msg}";
                        await _logService.LogContent($"{job.ToString()},{IotMsgConst.ScRemoteError}，{result.Msg}",  LogEnum.Error, job.EquipNum.ToString(), true);


                        _warns.Add(new IotEquipWarnDto
                        {
                            EquipType = ProcessType.ToString(),
                            ErrorCode = IotMsgConst.ScRemoteError + result.Msg,
                            ErrorTime = DateTime.Now,
                            EquipNum = job.EquipNum.ToString()
                        });
                        job.IsError = 999;



                    }
                });



            }




        }
        catch (Exception ex)
        {
            await _logService.LogContent($"{job.ToString()} 堆垛机出库任务 刷新数据失败，{ex.Message}",  LogEnum.Error, job.EquipNum.ToString());

        }


    }

    /// <summary>
    /// 入库启动
    /// </summary>
    /// <returns></returns>
    private async Task<bool> ScRunEnter(ScEnterJob job)
    {

        try
        {
            var dicKey = string.Empty;
            if (job.JobID2.IsNotNullOrEmpty())
            {
                dicKey = $"{job.JobID}-{job.JobID2}";

            }
            else
            {
                dicKey = $"{job.JobID}";
            }

            var tag =_processDataService.CvUnitTags.GetValueOrDefault(job.FromTagNumber);

          

            // 站台是否有托盘
            if (_equipValidateService.ValidateCV(tag) == false)
            {
                return false;
            }

            if (tag.WriteJobNumber != job.JobNumber)
            {
                job.ErrorMsg = IotMsgConst.ScEnterNumberError;
                job.IsError = 888;
                return false;
            }



            var sctag = _processDataService.ScUnits.GetValueOrDefault(job.EquipNum);

            if (sctag == null)
            {
                return false;
            }

            if (sctag.JobEndFork1 == 0 || sctag.JobEndFork2 == 0 || sctag.SC_State != 1)
            {
                return false;
            }


            // 如果存在任务，返回
            if (sctag == null || sctag.JobID.IsNotNullOrEmpty())
            {
                return false;
            }
            // 堆垛机没有复位
            if (sctag.W_JobNumber != 0 && sctag.W_JobNumber != 9999)
            {
                return false;
            }


            // 找到出库任务
            var scoutJob = _jobService.ScOutJobDic.Values.OrderBy(x => x.CreateTime).FirstOrDefault(x => x.EquipNum == job.EquipNum && x.Status == 0 && x.IsError == 0);
            if (scoutJob != null)
            {
                // 刚刚进行过入库，优先出库
                if (sctag.ActionType == 1)
                {
                    return false;
                }
                // 出库时间小于入库，优先出库
                if (scoutJob.CreateTime < job.CreateTime)
                {
                    return false;
                }
            }




           if(_equipValidateService.ValidateStacker(sctag) == false)
            {
                return false;
            }



            // 双叉任务
            if (job.JobID2.IsNotNullOrEmpty())
            {
                var tag2 = _processDataService.CvUnitTags.GetValueOrDefault(job.FromTagNumber2);

                if (tag2.JobNumber == 0) return false;

                // 站台是否有托盘
               if(_equipValidateService.ValidateCV(tag2) == false)
                {
                    return false;
                }

            }

           

            // 查询job的状态，确认是否运行
            var scRemote = await _remoteHttpService.GetScRemoteTask(job.JobID);
            if (scRemote.Data == null && scRemote.Code == 100)
            {
                return false;
            }
            if (scRemote.Data == null)
            {
                // await _serverDataService.RemoveEnterJob(job);
                job.ErrorMsg = IotMsgConst.ScEnterRemoteError;
                job.IsError = 888;
                return false;
            }
            else if (scRemote.Data.TaskStatus != 1)
            {
                job.ErrorMsg = $"{IotMsgConst.ScEnterRemoteError} {scRemote.Data.TaskStatus}";
                job.IsError = 888;
                return false;
            }
            job.Data = string.Join(',', scRemote.Data.TrayNos.Where(x => x.IsNotNullOrEmpty()));

            var plcSc = new PlcSCAddr
            {
                ActionType = 1,
                FromStation = job.FromStation,
                JobNumber = job.JobNumber,
                ToBank = job.ToBank,
                ToBay = job.ToBay,
                ToLevel = job.ToLevel,
                GroupNumber = job.EquipNum,
                FromStation2 = job.FromStation2,
                ToBank2 = job.ToBank2,
                ToBay2 = job.ToBay2,
                ToLevel2 = job.ToLevel2,
                JobNumber2 = job.JobNumber2

            };
          var msg=await  _processDataService.WriteScAddr(sctag,plcSc);

            if (msg.IsNotNullOrEmpty())
            {
                job.ErrorMsg = msg;
                // 写入失败
                return false;
            }

            plcSc.WriteTime = DateTime.Now;

            if (!RunTaskDic.ContainsKey(plcSc.JobNumber))
            {
                RunTaskDic.TryAdd(plcSc.JobNumber, plcSc);
            }

            //任务赋值
            sctag.JobID = job.JobID;

            job.RunTime = DateTime.Now;

            job.ErrorMsg = "";

            job.Status = 1;

            // 进行入库
            sctag.ActionType = 1;

            await _logService.LogContent($"{job.ToString()} 堆垛机开始入库成功",  LogEnum.Info, job.EquipNum.ToString());

            



            return true;
        }
        catch (Exception ex)
        {
            job.ErrorMsg = $"启动失败,{ex.Message}";

            return false;
        }

    }

    /// <summary>
    /// 入库完成
    /// </summary>
    /// <returns></returns>
    private async Task ScEnterComplete(ScEnterJob job)
    {
        // 不重试，直接退出
        if (job.RetryCount > 0)
            return;
        try
        {
            // 15s内不可能完成
            if (DateTime.Now.Subtract(job.RunTime).TotalSeconds < 15)
            {
                return;
            }
            var tag = _processDataService.CvUnitTags.GetValueOrDefault(job.FromTagNumber);


            string cacheKey = string.Empty;

            string dicKey = string.Empty;

            if (job.JobID2.IsNotNullOrEmpty())
            {
                cacheKey = $"{job.JobID}-{job.JobID2}";
                dicKey = $"{job.JobID}-{job.JobID2}";
            }
            else
            {
                cacheKey = $"{job.JobID}";
                dicKey = job.JobID;
            }


            var sctag = _processDataService.ScUnits.GetValueOrDefault(job.EquipNum);
            if (sctag == null) return;
            if (job.CreateTime != null && DateTime.Now.Subtract(job.CreateTime.Value).TotalMinutes > 2)
            {
                job.IsError = sctag.ErrorCode;
            }


            if (sctag.SC_State == 1 && sctag.JobEndFork2 == 1 && sctag.JobEndFork1 == 1 && sctag.W_JobNumber == job.JobNumber && job.JobNumber > 0 && sctag.ErrorCode == 0 && tag.ErrorCode == 0)
            {

                RemoteReturnModel<EcsWriteData> result2 = new RemoteReturnModel<EcsWriteData>()
                {
                    Flag = true
                };


                job.RetryCount++;

                // 任务ID清空，可以启动其他任务
                sctag.JobID = String.Empty;

                sctag.JobIDOld = sctag.JobID;

                job.Status = 2;

                // 联动充放电
                if (job.StageKey.IsNotNullOrEmpty())
                {
                    var fmt1 = _processDataService.HkFMTAppointDic.GetValueOrDefault(job.StageKey);
                    if (fmt1 != null)
                    {
                        fmt1.Status = 2;
                    }

                }
                if (job.StageKey2.IsNotNullOrEmpty())
                {
                    var fmt2 = _processDataService.HkFMTAppointDic.GetValueOrDefault(job.StageKey2);
                    if (fmt2 != null)
                    {
                        fmt2.Status = 2;
                    }
                }
                //开任务进行回调
                Task.Factory.StartNew(async () =>
                {
                    // 双叉任务
                    if (job.JobID2.IsNotNullOrEmpty())
                    {
                        result2 = await _remoteHttpService.CommitScTaskID(new ScTaskDefine
                        {
                            TaskId = long.Parse(job.JobID2),
                            OptType = 1,
                            TaskNumber = job.JobNumber2
                        });

                    }

                    // 调用远程接口
                    var result = await _remoteHttpService.CommitScTaskID(new ScTaskDefine
                    {
                        TaskId = long.Parse(job.JobID),
                        OptType = 1,
                        TaskNumber = job.JobNumber
                    });

                    if (result.Flag && result2.Flag)
                    {


                        // 成功后 清除缓存
                        await _logService.LogContent($"{job.ToString()},堆垛机入库任务 通知完成状态成功",  LogEnum.Info, job.EquipNum.ToString());
                        job.Status = 2;
                        if (_jobService.ScEnterJobDic.ContainsKey(dicKey))
                        {
                            _jobService.ScEnterJobDic.TryRemove(dicKey, out _);
                        }
                        //await _cacheService.RemoveAsync(cacheKey);
                       // await _serverDataService.RemoveScEnterJobFromRedis(job);
                    }
                    else
                    {

                        job.ErrorMsg = $"，{result.Msg} {result2.Msg}";
                        await _logService.LogContent($"{job.ToString()},堆垛机入库任务 通知完成状态失败,{result.Msg}",  LogEnum.Error, job.EquipNum.ToString(), true);

                        // await _cacheService.SetCacheAsync(cacheKey, job);

                        _warns.Add(new IotEquipWarnDto
                        {
                            EquipType = ProcessType.ToString(),
                            ErrorCode = $"{IotMsgConst.ScRemoteError} " + result.Msg,
                            ErrorTime = DateTime.Now,
                            EquipNum = job.EquipNum.ToString(),
                           
                        }) ;
                       

                        job.IsError = 999;


                    }
                });


            }




        }
        catch (Exception ex)
        {
            await _logService.LogContent($"{job.ToString()} 堆垛机入库任务 刷新数据失败，{ex.Message}",  LogEnum.Error, job.EquipNum.ToString());

        }

    }


    /// <summary>
    /// 移库启动
    /// </summary>
    /// <returns></returns>
    private async Task<bool> ScMoveEnter(ScMoveJob job)
    {

        try
        {
            var dicKey = string.Empty;

            dicKey = $"{job.JobID}";






            var sctag = _processDataService.ScUnits.GetValueOrDefault(job.EquipNum);

            if (sctag.JobEndFork1 == 0 || sctag.JobEndFork2 == 0 || sctag.SC_State != 1)
            {
                return false;
            }

            if (sctag == null)
            {
                return false;
            }


            // 找到出库任务
            var scoutJob = _jobService.ScOutJobDic.Values.OrderBy(x => x.CreateTime).FirstOrDefault(x => x.EquipNum == job.EquipNum && x.Status == 0 && x.IsError == 0);
            var scenterJob = _jobService.ScEnterJobDic.Values.OrderBy(x => x.CreateTime).FirstOrDefault(x => x.EquipNum == job.EquipNum && x.Status == 0 && x.IsError == 0);
            if (scoutJob != null)
            {
                // 出库时间小于入库，优先出库
                if (scoutJob.CreateTime < job.CreateTime)
                {
                    return false;
                }


            }
            if (scenterJob != null)
            {
                if (scenterJob.CreateTime < job.CreateTime)
                {
                    return false;
                }
            }

            // 如果存在任务，返回
            if (sctag == null || sctag.JobID.IsNotNullOrEmpty())
            {
                return false;
            }
            // 堆垛机没有复位
            if (sctag.W_JobNumber != 0 && sctag.W_JobNumber != 9999)
            {
                return false;
            }

           if(_equipValidateService.ValidateStacker(sctag) == false)
            {
                return false;
            }


            

            // 查询job的状态，确认是否运行
            var scRemote = await _remoteHttpService.GetScRemoteTask(job.JobID);
            if (scRemote.Data == null && scRemote.Code == 100)
            {
                return false;
            }
            if (scRemote.Data == null)
            {
                // await _serverDataService.RemoveMoveJob(job);
                job.ErrorMsg = IotMsgConst.ScEnterRemoteError;
                job.IsError = 888;
                return false;
            }
            else if (scRemote.Data.TaskStatus != 1)
            {
                job.ErrorMsg = $"{IotMsgConst.ScEnterRemoteError}{scRemote.Data.TaskStatus}";
                job.IsError = 888;
                return false;
            }

            job.Data = string.Join(',', scRemote.Data.TrayNos.Where(x => x.IsNotNullOrEmpty()));

            var plcSc = new PlcSCAddr
            {
                ActionType = 3,
                FromBank = job.FromBank,
                FromBay = job.FromBay,
                FromLevel = job.FromLevel,
                JobNumber = job.JobNumber,
                ToBank = job.ToBank,
                ToBay = job.ToBay,
                ToLevel = job.ToLevel,
                GroupNumber = job.EquipNum,

            };

           var msg= await _processDataService.WriteScAddr(sctag, plcSc);

            if (msg.IsNotNullOrEmpty())
            {
                job.ErrorMsg = msg;
                return false;
            }

            plcSc.WriteTime = DateTime.Now;

            if (!RunTaskDic.ContainsKey(plcSc.JobNumber))
            {
                RunTaskDic.TryAdd(plcSc.JobNumber, plcSc);
            }



            //任务赋值
            sctag.JobID = job.JobID;

            job.RunTime = DateTime.Now;

            job.ErrorMsg = "";

            job.Status = 1;

            // 进行移库
            sctag.ActionType = 3;

            await _logService.LogContent($"{job.ToString()} 堆垛机开始移库成功",  LogEnum.Info, job.EquipNum.ToString());





            return true;
        }
        catch (Exception ex)
        {
            job.ErrorMsg = $"无法启动" + ex.Message;

            return false;
        }

    }

    /// <summary>
    /// 移库完成
    /// </summary>
    /// <returns></returns>
    private async Task ScMoverComplete(ScMoveJob job)
    {
        // 开启入库完成任务
        if (job.RetryCount > 0)
        {
            return;
        }

        try
        {
            // 15s内不可能完成
            if (DateTime.Now.Subtract(job.RunTime).TotalSeconds < 15)
            {
                return;
            }



            string cacheKey = string.Empty;

            string dicKey = string.Empty;

            cacheKey = $"{job.JobID}";
            dicKey = job.JobID;



            var sctag = _processDataService.ScUnits.GetValueOrDefault(job.EquipNum);
            if (sctag == null) return;
            if (job.CreateTime != null && DateTime.Now.Subtract(job.CreateTime.Value).TotalMinutes > 2)
            {
                job.IsError = sctag.ErrorCode;
            }


            if (sctag.SC_State == 1 && sctag.JobEndFork2 == 1 && sctag.JobEndFork1 == 1 && sctag.W_JobNumber == job.JobNumber && job.JobNumber > 0 && sctag.ErrorCode == 0)
            {

                RemoteReturnModel<EcsWriteData> result2 = new RemoteReturnModel<EcsWriteData>()
                {
                    Flag = true
                };

                job.RetryCount++;

                // 任务ID清空，可以启动其他任务
                sctag.JobID = String.Empty;

                sctag.JobIDOld = sctag.JobID;

                job.Status = 2;

                Task.Factory.StartNew(async () =>
                {
                    // 调用远程接口
                    var result = await _remoteHttpService.CommitFmtMoveJob(new ScTaskDefine
                    {
                        TaskId = long.Parse(job.JobID),
                        OptType = 3,
                        TaskNumber = job.JobNumber
                    });

                    if (result.Flag)
                    {


                        // 成功后 清除缓存
                        await _logService.LogContent($"{job.ToString()},堆垛机移库任务 通知完成状态成功",  LogEnum.Info, job.EquipNum.ToString());

                        if (_jobService.ScMoveJobDic.ContainsKey(dicKey))
                        {
                            _jobService.ScMoveJobDic.TryRemove(dicKey, out _);
                        }
                       // await _cacheService.RemoveAsync(cacheKey);

                    }
                    else
                    {

                        job.ErrorMsg = $"{IotMsgConst.ScRemoteError} {result.Msg}";
                        await _logService.LogContent($"{job.ToString()},堆垛机移库任务 通知完成状态失败,{result.Msg}",  LogEnum.Error, job.EquipNum.ToString(), true);

                        // await _cacheService.SetCacheAsync(cacheKey, job);

                        _warns.Add(new IotEquipWarnDto
                        {
                            EquipType = ProcessType.ToString(),
                            ErrorCode = "移库回调失败 " + result.Msg,
                            ErrorTime = DateTime.Now,
                            EquipNum = job.EquipNum.ToString()
                        });
                       

                        job.IsError = 999;


                    }
                });


            }




        }
        catch (Exception ex)
        {
            await _logService.LogContent($"{job.ToString()} 堆垛机移库任务 刷新数据失败，{ex.Message}",  LogEnum.Error, job.EquipNum.ToString());

        }

    }

    public bool Stop()
    {
        _processDataService.ScUnits.Clear();
        return true;
    }





    #endregion
}
